package com.example.synchronizedLearning;

/**
 * 不管是类锁还是对象锁其实都是获取一个锁将对应的一个对象或者一个类进行上锁，防止其他的线程再次使用该对象或类，达到同步的效果
 * ✔️ Java 中的同步（synchronized）机制是通过“获取某个对象或类的监视器锁”，确保在某一时刻，只有一个线程能访问被同步的资源（方法或代码块），从而防止数据冲突，确保线程安全。
 * 锁就像一个标识符，只有取到该标识符才能对对应的代码块进行操作，锁住的区域恰好是该代码块
 */
public class Syn7 {
    /**
     * static 关键字的作用是使方法或变量归属于类级别，而不是实例级别。
     * synchronized(Syn7.class) 的本质是：
     * 在 Java 的 JVM 规范中，每个类在被加载后，JVM 会为其在**方法区（Method Area）**创建一个唯一的 Class 对象。
     * Syn7.class 就是这个唯一存在的 Class 对象。
     * synchronized(Syn7.class) 表示在线程执行 print() 方法时，线程必须首先获得这个类对象的锁。
     * synchronized(Syn7.class) 与 static 无关，因为它直接操作的是类对象（Class对象）。
     */
    /**
     * synchronized(Syn7.class) 加锁的对象在方法区中，属于类级别的锁。
     * synchronized(this) 加锁的对象在堆中，属于对象级别的锁。
     */
    public void print() {
        /**
         * obj1.print() 和 obj2.print() 是不同对象的实例方法。
         * 但由于使用的是 synchronized(Syn7.class)，因此两个线程在竞争的其实是类对象（Class对象）。
         * JVM 中只有一个 Syn7.class 对象，因此两个线程是互斥的。
         *
         * 线程 t1 和 t2 在执行时，会去尝试获取方法区中唯一的 Syn7.class 监视器锁；
         * 因此不管谁先调用，另一个线程必须等待类锁释放才能进入；
         * 从而实现了同步（互斥）效果。
         *
         * 你创建了两个对象（obj1 和 obj2），它们是独立的实例。
         * 但是你在方法中锁住的是类对象（Syn7.class），这是所有对象共有的一把锁。
         * 不是在对象创建时上锁，而是在线程执行 synchronized(Syn7.class) 的时候，尝试加锁。
         * 多个线程执行这个方法时会因为争夺同一把类锁而产生同步效果。
         */
        synchronized (Syn7.class) { // 显式类锁
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + " - " + i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

/**
 * 方式	                        锁对象	        锁范围	        互斥性
 * synchronized(this)	        对象实例（堆内存）	只对当前对象有效	只在当前对象级别互斥
 * synchronized(Class.class)	类对象（方法区）	影响所有对象	    在类级别互斥
 * synchronized static	        类对象（方法区）	影响所有对象	    在类级别互斥
 */

/**
 * ✅ synchronized(Syn7.class) 是为了在执行 print() 方法前，在线程级别上获得 Syn7.class 的互斥锁。
 * ✅ print() 方法虽然被 synchronized(Syn7.class) 保护，但它本质上仍然是实例方法，因此它的代码逻辑和对象引用仍然位于**堆（Heap）**中。
 * ✅ print() 方法是在对象实例上执行的，而不是在类对象（Class对象）上执行的。
 */
class syn7Test {
    public static void main(String[] args) {
        Syn7 obj1 = new Syn7();
        Syn7 obj2 = new Syn7();

        /**
         * 不管你是用类名还是对象来调用静态方法，本质上调用的都是“类方法”，与对象无关。
         */
        // 即使是不同对象，锁住类对象也会互斥
        Thread t1 = new Thread(obj1::print);
        Thread t2 = new Thread(obj2::print);

        /**
         * synchronized(MyClass.class) 锁住了类对象 MyClass.class。
         * 即使是不同对象（obj1 和 obj2），由于锁住的是类对象，线程必须互斥访问。
         * t1 释放类锁后，t2 才能继续。
         */
        t1.start();
        t2.start();
    }
}
